#ifndef PW_PBL_SPAN_HPP
#define PW_PBL_SPAN_HPP

#include <ranges>

namespace pw_pbl
{
	template <typename T, size_t... N> requires(sizeof...(N) <= 1)
	struct Span;

	template <typename T>
	struct Span<T>
	{
		T *_pData;
		size_t _size;

		template <std::ranges::range RANGE>
		constexpr Span(RANGE &&rng) noexcept : _pData{std::ranges::begin(rng)}, _size{std::ranges::size(rng)} {}
		constexpr Span(T *be, size_t sz) noexcept : _pData{be}, _size{sz} {}
		constexpr T *begin() const noexcept { return _pData; }
		constexpr T *end() const noexcept { return _pData + _size; }
		constexpr size_t size() const noexcept { return _size; }
		constexpr T *data() const noexcept { return _pData; }
		constexpr T &operator[](size_t i) const noexcept { return _pData[i]; }
		template <typename O> requires(alignof(O) >= alignof(T) && sizeof(O) % sizeof(T) == 0 && !std::ranges::range<O>)
		static Span force_bind(O &obj) { return {(T *)&obj, sizeof(O) / sizeof(T)}; }
		static Span null() { return {nullptr, 0}; }
	};

	template <typename T, size_t N>
	struct Span<T, N>
	{
		T *_pData;

		template <typename RANGE>
		explicit constexpr Span(RANGE &&rng) noexcept : _pData{std::ranges::begin(rng)} {}
		constexpr Span(T (&data)[N]) noexcept : _pData{data} {}
		constexpr Span(std::array<T, N> &data) noexcept : _pData{data.data()} {}
		constexpr Span(const std::array<std::remove_const_t<T>, N> &data) noexcept requires std::is_const_v<T> : _pData{data.data()} {}
		constexpr T *begin() const noexcept { return _pData; }
		constexpr T *end() const noexcept { return _pData + N; }
		constexpr size_t size() const noexcept { return N; }
		constexpr T *data() const noexcept { return _pData; }
		constexpr T &operator[](size_t i) const noexcept { return _pData[i]; }
		constexpr operator std::span<T, N>() const noexcept { return std::span<T, N>{*this}; }
		template <typename O> requires(alignof(O) >= alignof(T) && sizeof(O) >= sizeof(T) * N && !std::ranges::range<O>)
		static Span force_bind(O &obj) { return {(T *)&obj, N}; }
	};

} // namespace pw_pbl

#endif // PW_PBL_SPAN_HPP
